// PictureEdit.cpp : implementation file
//

#include "stdafx.h"
#include "PictureEdit.h"


// CPictureEdit
#define XPOSDIFF	3
CPictureEdit::CPictureEdit()
{
	m_pMemDestDC = NULL;
	m_pMemDestBitmap = NULL;

	m_nCurPos = 0;

	m_vecRect.clear();
}

CPictureEdit::~CPictureEdit()
{
	if(m_pMemDestDC) {
		CBitmap* pOldBmp = (CBitmap*)m_pMemDestDC->SelectObject((CBitmap*)0);
		DeleteDC(m_pMemDestDC->GetSafeHdc());  delete m_pMemDestDC;  m_pMemDestDC = NULL;
	}
	if(m_pMemDestBitmap) { delete m_pMemDestBitmap;  m_pMemDestBitmap = NULL; }
}

BEGIN_MESSAGE_MAP(CPictureEdit, CEdit)
	ON_WM_PAINT()
	ON_CONTROL_REFLECT(EN_CHANGE, &CPictureEdit::OnEnChange)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_CTLCOLOR_REFLECT()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_RBUTTONDBLCLK()
	ON_WM_RBUTTONUP()
	ON_WM_KEYDOWN()
	ON_WM_CHAR()	
	ON_WM_SETFOCUS()
END_MESSAGE_MAP()

// CPictureEdit message handlers

void CPictureEdit::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	// TODO: Add your message handler code here
	// Do not call CEdit::OnPaint() for painting messages
	
	DrawBackground();
	DrawControl();
	dc.BitBlt(0, 0, m_rectWindow.Width(), m_rectWindow.Height(), m_pMemDestDC, 0, 0, SRCCOPY);
	DrawString(dc);
}

HBRUSH CPictureEdit::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
	pDC->SetTextColor(RGB(81, 81, 81));
	pDC->SetBkColor(RGB(255, 255, 255));
	return HBRUSH(GetStockObject(NULL_BRUSH));
}

void CPictureEdit::GetCharPos()
{
	m_vecRect.clear();
	if(m_pMemDestDC == NULL) {
		DrawBackground();
		DrawControl();
	}

	CRect rect;  GetClientRect(&rect);
	PointF pt((REAL)rect.left, (REAL)rect.top);
	RectF rectDraw, rectOrg(REAL(rect.left), REAL(rect.top), REAL(rect.Width()), REAL(rect.Height())), rectBound;

	const int nMaxChar = 32; // SetMeasurableCharacterRanges max count = 32, if over, then cause overflow error
	Region stringRegions[nMaxChar];
	CharacterRange characterRanges[nMaxChar];

	for(int i = 0; i < nMaxChar; i++) {
		characterRanges[i].First = i;
		characterRanges[i].Length = 1;
	}

	StringFormat strFormat(&m_nStringFormat);
	strFormat.SetMeasurableCharacterRanges(nMaxChar, characterRanges);
	strFormat.SetAlignment(m_nStringFormat.GetAlignment());
	if(strFormat.GetAlignment() != StringAlignmentCenter)
		rect.left = XPOSDIFF;

	CString strText;  GetWindowText(strText);
	if(GetStyle() & ES_PASSWORD) {
		CString strTmp;
		for(int i = 0; i < strText.GetLength(); ++i)
			strTmp += GetPasswordChar();
		strText = strTmp;
	}
	Gdiplus::Font gdifont(m_pFontFamily, (REAL)m_nFontSize, m_nFontStyle, m_nFontUnit);

	Graphics graphics(m_pMemDestDC->GetSafeHdc());
	graphics.MeasureString(strText, -1, &gdifont, pt, &strFormat, &rectBound);		

	int j = 0, nStartPos = 0, nTotalStringLength = strText.GetLength();
	BOOL bFinish = FALSE;
	while(!bFinish && nTotalStringLength > 0) {
		graphics.MeasureCharacterRanges(strText.Mid(nStartPos), -1, &gdifont, rectOrg, &strFormat, nMaxChar, stringRegions);

		for(j = 0 ; j < nMaxChar && nStartPos < nTotalStringLength; j++) {
			stringRegions[j].GetBounds(&rectDraw, &graphics);
			m_vecRect.push_back(rectDraw);

			nStartPos++;
			if(m_vecRect.size() == nTotalStringLength) {
				bFinish = TRUE;
				break;
			}
		}
		rectOrg.X += rectDraw.GetRight();
	}

	if(m_vecRect.size() == 0)
		m_nCurPos = 0;
}

POINT CPictureEdit::GetDefaultPos()
{
	POINT pt = {0, 0};
	if(m_pMemDestDC == NULL) {
		DrawBackground();
		DrawControl();
	}

	CRect rect;  GetClientRect(&rect);
	RectF rectDraw, rectOrg(REAL(rect.left), REAL(rect.top), REAL(rect.Width()), REAL(rect.Height()));

	// SetMeasurableCharacterRanges max count = 32, if over, then cause overflow error
	if(m_nStringFormat.GetAlignment() != StringAlignmentCenter)
		rectOrg.X = XPOSDIFF;
	const int nMaxChar = 1; 
	CharacterRange characterRanges(0, 1);
	StringFormat strFormat(&m_nStringFormat);
	strFormat.SetAlignment(m_nStringFormat.GetAlignment());
	strFormat.SetMeasurableCharacterRanges(nMaxChar, &characterRanges);

	Region stringRegions;
	CString strText = L"1";
	Gdiplus::Font gdifont(m_pFontFamily, (REAL)m_nFontSize, m_nFontStyle, m_nFontUnit);
	Graphics graphics(m_pMemDestDC->GetSafeHdc());
	graphics.MeasureCharacterRanges(strText, 1, &gdifont, rectOrg, &strFormat, nMaxChar, &stringRegions);

	stringRegions.GetBounds(&rectDraw, &graphics);
	if(m_nStringFormat.GetAlignment() != StringAlignmentCenter)
		pt.x = (LONG)rectDraw.GetLeft();
	else
		pt.x = rect.Width()/2;
	
	pt.y = (LONG)rectDraw.GetTop();
	return pt;
}

void CPictureEdit::MoveCaret(int nPos)
{
	int nCount = (int)m_vecRect.size();
	POINT pt = GetCaretPos();
	if(nPos >= 0 && nPos <= nCount -1) {
		pt.x = (LONG)m_vecRect[nPos].GetLeft();
		pt.y = (LONG)m_vecRect[nPos].GetTop();
	}
	else if(nPos == nCount && nCount > 0) {
		pt.x = (LONG)m_vecRect[nCount - 1].GetRight();
		pt.y = (LONG)m_vecRect[nCount - 1].GetTop();
	}
	else  pt = GetDefaultPos();

	SetCaretPos(pt);
	Invalidate(FALSE);
}

void CPictureEdit::SetCaret(CPoint point)
{
	int nCount = (int)m_vecRect.size();
	POINT pt;

	if(nCount > 0) {
		pt.y = (LONG)m_vecRect[0].GetTop();
		BOOL bFind = FALSE;
		
		for(int i = 0; i < nCount; ++i) {
			if(m_vecRect[i].Contains((REAL)point.x, (REAL)point.y)) {
				m_nCurPos = i;
				pt.x = (LONG)m_vecRect[i].GetLeft();
				bFind = TRUE;
				break;
			}
		}

		if(!bFind) {
			if(point.x < m_vecRect[0].GetLeft()) {
				m_nCurPos = 0;
				pt.x = (LONG)m_vecRect[0].GetLeft();
			}
			else if(point.x > m_vecRect[nCount-1].GetRight()) {
				m_nCurPos = nCount;
				pt.x = (LONG)m_vecRect[nCount-1].GetRight();
			}
		}
	}
	else  pt = GetDefaultPos();

	SetCaretPos(pt);
	Invalidate(FALSE);
}

void CPictureEdit::OnEnChange()
{
	Invalidate(FALSE);
	GetCharPos();
}

void CPictureEdit::OnLButtonDown(UINT nFlags, CPoint point)
{
	if(m_vecRect.size() == 0)
		GetCharPos();

	SetFocus();
	SetSel(0, FALSE);
	SetCaret(point);	
//	block left button down
//	CEdit::OnLButtonDown(nFlags, point);
}

void CPictureEdit::OnLButtonUp(UINT nFlags, CPoint point)
{
//	CEdit::OnLButtonUp(nFlags, point);
}

void CPictureEdit::OnLButtonDblClk(UINT nFlags, CPoint point)
{
//	CEdit::OnLButtonDblClk(nFlags, point);
}

void CPictureEdit::OnRButtonDblClk(UINT nFlags, CPoint point)
{
//	CEdit::OnRButtonDblClk(nFlags, point);
}

void CPictureEdit::OnRButtonUp(UINT nFlags, CPoint point)
{
//	CEdit::OnRButtonUp(nFlags, point);
}

void CPictureEdit::OnMouseMove(UINT nFlags, CPoint point)
{
//	CEdit::OnMouseMove(nFlags, point);
}

void CPictureEdit::DeleteChar(int nPos)
{
	CString str;
	GetWindowText(str);
	if(!str.IsEmpty()) {
		str.Delete(nPos);
		SetWindowText(str);
		MoveCaret(nPos);
	}
	else  MoveCaret(-1);
}

void CPictureEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	int nCount = (int)m_vecRect.size();

	switch(nChar) {
	case VK_UP:
	case VK_LEFT:
		if(m_nCurPos != 0)
			MoveCaret(--m_nCurPos);
		break;
	case VK_RIGHT:
	case VK_DOWN:
		if(m_nCurPos < nCount)
			MoveCaret(++m_nCurPos);
		else if(m_nCurPos == nCount)
			MoveCaret(nCount);
		break;
	case VK_HOME:
		m_nCurPos = 0;
		MoveCaret(m_nCurPos);
		break;
	case VK_END:
		m_nCurPos = nCount - 1;
		MoveCaret(nCount);
		break;
	case VK_DELETE:
		DeleteChar(m_nCurPos);
		break;
	case VK_BACK:
		if ( m_nCurPos > 0 )
			DeleteChar(--m_nCurPos);
		break;
	case VK_INSERT:
		break;
	default:
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
	}
}

void CPictureEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if((GetStyle()&ES_NUMBER) && !_istdigit(nChar)) {
		if(!(GetKeyState(VK_CONTROL)&0x8000 || GetKeyState(VK_MENU) & 0x8000))
			CEdit::OnChar(nChar, nRepCnt, nFlags);
		return;
	}

	if(_istprint(nChar)) {
		CString str;  GetWindowText(str);
		int nLimit = GetLimitText();
		if(str.GetLength() < nLimit) {
			str.Insert(m_nCurPos++, nChar);
			SetWindowText(str);
			MoveCaret(m_nCurPos);
		}
	}
}

void CPictureEdit::OnSetFocus(CWnd* pOldWnd)
{
	CEdit::OnSetFocus(pOldWnd);
	MoveCaret(-1);
}

BOOL CPictureEdit::PreTranslateMessage(MSG* pMsg)
{
	if(pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB)
		return TRUE;

	return CEdit::PreTranslateMessage(pMsg);
}

void CPictureEdit::DrawBackground()
{
	if(NULL == m_pMemBgDC) {
		CDialogExt* wndDlgex = (CDialogExt*)GetParent();
		m_pMemBgDC = wndDlgex->GetDialogCdc();

		GetWindowRect(m_rectWindow);  wndDlgex->ScreenToClient(&m_rectWindow);
	}
}

void CPictureEdit::DrawControl()
{
	if(NULL == m_pMemDestDC) {
		CDC* tmpDC = GetDC();
		m_pMemDestDC = new CDC();
		m_pMemDestDC->CreateCompatibleDC(tmpDC);
		m_pMemDestBitmap = new CBitmap();
		m_pMemDestBitmap->CreateCompatibleBitmap(tmpDC, m_rectWindow.Width(), m_rectWindow.Height());
		m_pMemDestDC->SelectObject(m_pMemDestBitmap);
		ReleaseDC(tmpDC);
		
		CClientDC DC(this);
		m_pMemDestDC->BitBlt(0, 0, m_rectWindow.Width(), m_rectWindow.Height(), &DC, 0, 0, SRCCOPY);
	}
}

void CPictureEdit::DrawString(CDC& dc)
{
	GetWindowTextW(m_strWindowText);
	if(!m_strWindowText.IsEmpty()){
		if(GetStyle() & ES_PASSWORD) {
			int nlen = m_strWindowText.GetLength();
			m_strWindowText.Empty();
			TCHAR chpw = GetPasswordChar();
			while(nlen--)
				m_strWindowText += chpw;
		}

		Graphics graphics(dc.GetSafeHdc());
		graphics.DrawString(m_strWindowText,
			-1,
			m_pFont,
			RectF((REAL)0,(REAL)0,(REAL)m_rectWindow.Width(),(REAL)m_rectWindow.Height()),
			&m_nStringFormat,
			m_pBrush);
	}
}